/**
 * Name: Avinash Sridhar	G Number: G00861300
 */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class TokenRequestHandler extends Thread {

	private RMIServer mServer;
	
	private ServerSocket mTokenHandlerServerSocketPort;	//coming from the left neighbor

	public InetAddress server_addr = InetAddress.getByName("localhost");
	
	public TokenRequestHandler(RMIServer server) throws IOException
	{
		mServer = server;
		mTokenHandlerServerSocketPort = new ServerSocket(mServer.getmCurrDeptModule().mTokenSocketPortNumber);	//going to be listening on your Token socket port
		System.out.println("Listening for Tokens at port <" + mTokenHandlerServerSocketPort.getLocalPort() + ">...");
	}

	@Override
	public void run() 
	{
		while (true) {
			try{ 
	
				if (mServer.mInitialTokenPossesion)	//if you are the first server being initiated... you have the token > do your stuff > pass off token
				{
					//send START_TIMER to right neighbor...."ST*5* + left modules info"
					if (mServer.getmHasRequests())
						WriteToPort("ST$" + (mServer.CriticalSectionDelay + mServer.ProcessingDelay) + "$" + mServer.getmCurrDeptModule().toString(), mServer.getmRightDeptModule());
					else
						WriteToPort("ST$" + mServer.ProcessingDelay + "$" + mServer.getmCurrDeptModule().toString(), mServer.getmRightDeptModule());
	
					//wait for OK reply within ~ 0-1 seconds MAX
					System.out.println("Going to wait for 1 second starting " + System.currentTimeMillis());
					mTokenHandlerServerSocketPort.setSoTimeout(1000);
	
					String okay = "";
					try {
	
						okay = ReadFromSocket(mTokenHandlerServerSocketPort.accept()); //blocking call (this time will block MAX FOR 1000 ms)
	
					} catch (SocketTimeoutException e)
					{
						System.out.println("Detected that Right Neighbor must be dead at t=0 at " + System.currentTimeMillis());

						//set right = right_right
						mServer.setmRightDeptModule(mServer.getmRight_RightDeptModule());
	
						//Wait for fix.......
						synchronized (this)
						{
							System.out.println("fixing ring...");
							while (mServer.getmRight_RightDeptModule().mName == null)
							{
								try {
									wait();
								} catch (InterruptedException e1) {}
							}
							System.out.println("ring fixed at " + System.currentTimeMillis());
						}
						
						//after fix.........
						//send START_TIMER to NEW RIGHT neighbor...."ST*5* + left modules info"
						if (mServer.getmHasRequests())
							WriteToPort("ST$" + (mServer.CriticalSectionDelay + mServer.ProcessingDelay) + "$" + mServer.getmCurrDeptModule().toString(), mServer.getmRightDeptModule());
						else
							WriteToPort("ST$" + mServer.ProcessingDelay + "$" + mServer.getmCurrDeptModule().toString(), mServer.getmRightDeptModule());
	
						//wait for OK reply within ~ 0-1 seconds MAX
						mTokenHandlerServerSocketPort.setSoTimeout(1000);
	
						try {
							okay = ReadFromSocket(mTokenHandlerServerSocketPort.accept()); //blocking call (this time will block MAX FOR 1000 ms)
						} catch (SocketTimeoutException e2) { e2.printStackTrace(); }	//should get "OK"...two in a row broken can't happen
					}
	
					if (okay.contains("OK"))	//got "OK" from either Right --or-- Right_Right (new Right)
					{
						//update right_right with info provided
						String[] temp = okay.split("#");
						mServer.setmRight_RightDeptModule(new DeptModuleInfo(temp[1], Integer.parseInt(temp[2])));
												
						// new thread to do (Processing + C.S.)
						new CriticalSectionService(mServer).start();
												
						//wait for RECVD_TOKEN within ~ 5-6 seconds MAX
						//---TODO-----
						//wait for the right amount of time
						System.out.println("Going to wait for 6 seconds starting " + System.currentTimeMillis());
						mTokenHandlerServerSocketPort.setSoTimeout(6000);
	
						String rcvd = "";
						try {
							
							rcvd = ReadFromSocket(mTokenHandlerServerSocketPort.accept()); //blocking call (this time will block MAX FOR 6000 ms)	
						
						} catch (SocketTimeoutException e)
						{
							System.out.println("Detected that Right Neighbor must have died between t=0 and t=5 at " + System.currentTimeMillis());
							
							//set right = right_right
							mServer.setmRightDeptModule(mServer.getmRight_RightDeptModule());
							
							//Wait for fix.......
							synchronized (this)
							{
								System.out.println("fixing ring...");
								while (mServer.getmRight_RightDeptModule().mName == null)
								{
									try {
										wait();
									} catch (InterruptedException e1) {}
								}
								System.out.println("ring fixed at " + System.currentTimeMillis());
							}
							
							//after fix.........
							//pass token on to NEW right instead
							//do not care about "RECVD"....2 Broken in a row (at the same time) can't happen
							WriteToPort("TOKEN", mServer.getmRightDeptModule());
							System.out.println("*******************");		
						}
						if (rcvd.equals("RCVD_TOKEN"))
						{	
							//cool man.
							System.out.println("*******************");
						}
					}
					mServer.mInitialTokenPossesion = false;	
				}
				
				
				
				//---------------------------WIHOUT TOKEN (ESSENTIALLY THE RIGHT NEIGHBOR)-------------------------------------
				
				
				
				else
				{
					//receive tokens from YOUR LEFT NEIGHBOR					
					String value = ReadFromSocket(mTokenHandlerServerSocketPort.accept());	//block forever until you get a ....."ST$5$#left_name#left_num"
	
					if (value.equals("TOKEN"))
					{
						//cool. cool. cool.
						
						//send back to the left "RCVD_TOKEN"
						WriteToPort("RCVD_TOKEN", mServer.getmLeftDeptModule());

						// do all the stuff from the first case
						mServer.mInitialTokenPossesion = true;
					}
					if (!value.equals("") && !value.equals("TOKEN"))
					{
						//update left with info provided
						String[] temp = value.split("#");
						mServer.setmLeftDeptModule(new DeptModuleInfo(temp[1], Integer.parseInt(temp[2])));
						
						//send back to the left "OK#right_name#right_num"
						WriteToPort("OK" + mServer.getmRightDeptModule().toString(), mServer.getmLeftDeptModule());
						
						//-----TODO--------
						//String[] timeSplit = value.split("$");
						
						//Start your timer for 5-6 seconds MAX to receive token from A 
						System.out.println("Going to wait for 6 seconds starting " + System.currentTimeMillis());
						mTokenHandlerServerSocketPort.setSoTimeout(6000);
	
						String token_val = "";
						try {
	
							token_val = ReadFromSocket(mTokenHandlerServerSocketPort.accept()); //blocking call (this time will block MAX FOR 1000 ms)	
	
						} catch (SocketTimeoutException e)
						{
							System.out.println("A must have died between t=0 and t=5");
	
							//We don't care about this....
	
							//A died without sending me the token
							System.out.println("Regenerating the token...Regenerated!");
							mServer.mInitialTokenPossesion = true;
						}
	
						if (token_val.equals("TOKEN"))
						{
							//cool. cool. cool.
	
							//send back to the left "RCVD_TOKEN"
							WriteToPort("RCVD_TOKEN", mServer.getmLeftDeptModule());
	
							// do all the stuff from the first case
							mServer.mInitialTokenPossesion = true;
						}
	
					}
	
				}
			} catch (IOException e) {
				//System.out.println("Could not listen on port " + mTokenHandlerServerSocketPort.getLocalPort());
				//e.printStackTrace();
				//continue;
				//System.exit(0);
			}
		}
	}
	
	public void WriteToPort(String stringToWrite, DeptModuleInfo dept)
	{
    	try {
    		Socket client = new Socket(server_addr, dept.mTokenSocketPortNumber);
			PrintWriter Writer = new PrintWriter(client.getOutputStream(), true);
			Writer.println(stringToWrite);
			
			System.out.println(mServer.getmCurrDeptModule().mName + " > " + stringToWrite + " > " + dept.mName + " at " + System.currentTimeMillis());
			
			client.close();
			Writer.close();
    		
    	} catch (IOException e) 
    	{ 
    		//e.printStackTrace();
    	}
	}
	
	public String ReadFromSocket(Socket socket)
	{
		String socketValue = "";
		try {
			
			BufferedReader Reader = new BufferedReader( new InputStreamReader(socket.getInputStream()) );
			socketValue = Reader.readLine();
			
			socket.close();
			Reader.close();
			
		} catch (IOException e) 
		{
			//e.printStackTrace();
		}
		
		System.out.println(mServer.getmCurrDeptModule().mName + " got " + socketValue + " at " + System.currentTimeMillis());
		
		return socketValue;
	}

}